package org.layuiframework.stereotype.security.impl;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.springframework.stereotype.Component;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * 记录异常登录
 */
@Component
public class LoginAttemptService {

    private final String USER_PREFIX = "sendmessage_user_login_error:";//redis key前缀

    private final int MAX_ATTEPT = 5;//连续异常登录最大次数

    private final int lockTime = 30 * 60 * 1000;

    private final LoadingCache<String, LockUser> atteptCache;

    public LoginAttemptService() {
        super();
        atteptCache = CacheBuilder
                .newBuilder()
                .expireAfterWrite(30, TimeUnit.MINUTES)//30分钟重置一次
                .build(new CacheLoader<String, LockUser>() {
                    @Override
                    public LockUser load(String arg0) throws Exception {
                        return new LockUser();
                    }

                });
    }

    /**
     * 清除登录成功的账号限制
     *
     * @param key
     */
    public void loginSuccessed(String key) {
        atteptCache.invalidate(USER_PREFIX + key);
    }

    /**
     * 记录连续登录异常次数
     *
     * @param key
     */
    public void loginFailed(String key) {
        LockUser user = null;
        Integer num = 0;
        try {
            user = atteptCache.get(USER_PREFIX + key);
            if (user == null || user.getErrorNum() == null) {
                num = 0;
            } else {
                num = user.getErrorNum();
            }
        } catch (Exception e) {
            num = 0;
        }
        num++;
        user = new LockUser(System.currentTimeMillis(), num);
        atteptCache.put(USER_PREFIX + key, user);
    }

    /**
     * 获取剩余锁定时间
     *
     * @param key
     * @return
     */
    public String getLockTime(String key) {

        StringBuffer str = new StringBuffer();
        LockUser user;
        Long time;
        Long cuTime = System.currentTimeMillis();
        try {
            user = atteptCache.get(USER_PREFIX + key);
            time = user != null ? user.getLockTime() : cuTime;
        } catch (ExecutionException e) {
            time = cuTime;
        }
        Integer dv = (int) (lockTime - (cuTime - time)) / 1000;
        Integer minute = dv / 60;
        Integer sec = dv % 60;

        str.append(minute > 9 ? minute : "0" + minute);
        str.append(":");
        str.append(sec > 9 ? sec : "0" + sec);

        return str.toString();
    }

    /**
     * 获取剩余锁定次数
     *
     * @param key
     * @return
     */
    public Integer getLockNum(String key) {
        StringBuffer str = new StringBuffer();
        LockUser user;
        int num;
        try {
            user = atteptCache.get(USER_PREFIX + key);
            num = user != null ? user.getErrorNum() : 0;
        } catch (ExecutionException e) {
            num = 0;
        }
        return MAX_ATTEPT - num;
    }

    /**
     * 验证账号时候被锁定
     *
     * @param key
     * @return
     */
    public boolean isBlocked(String key) {
        LockUser user = null;
        Integer num = 0;
        try {
            user = atteptCache.get(USER_PREFIX + key);
            num = user != null ? user.getErrorNum() : 0;
            return num >= MAX_ATTEPT;
        } catch (Exception e) {
            return false;
        }

    }


    public class LockUser {
        Long lockTime;//锁定时间
        Integer errorNum;//错误次数


        public LockUser() {
            super();
        }

        public LockUser(Long lockTime, Integer errorNum) {
            super();
            this.lockTime = lockTime;
            this.errorNum = errorNum;
        }

        public Long getLockTime() {
            return lockTime;
        }

        public void setLockTime(Long lockTime) {
            this.lockTime = lockTime;
        }

        public Integer getErrorNum() {
            return errorNum;
        }

        public void setErrorNum(Integer errorNum) {
            this.errorNum = errorNum;
        }

    }
}
